package com.sunfeng.jksj.p27;


import java.util.concurrent.locks.StampedLock;

/**
 * @Author : 孙峰
 * @Description: 锁的申请和释放要成对出现
 * 锁的申请和释放要成对出现，对此我们有一个最佳实践，就是使用 try{}finally{}，但是 try{}finally{}并不能解决所有锁的释放问题。
 * <p>
 * StampedLock  和 ReadWriteLock   区别
 * StampedLock  支持写锁，悲观读错和乐观读   乐观读锁是无锁的效率要高
 * ReadWriteLock 支持写锁和读锁
 * StampedLock 使用注意事项
 * 1、对于读多写少的场景 StampedLock 性能很好，简单的应用场景基本上可以替代 ReadWriteLock，但是 StampedLock 的功能仅仅是 ReadWriteLock 的子集
 * 2、StampedLock在命名上并没有增加Reentrant，所以StampedLock 不支持重入。
 * 3、StampedLock 的悲观读锁、写锁都不支持条件变量
 * 4、使用 StampedLock 一定不要调用中断操作，如果需要支持中断功能，一定使用可中断的悲观读锁 readLockInterruptibly() 和写锁 writeLockInterruptibly()。
 * <p>
 * StampedLockDemo中有相关使用方式
 * @Date : 2022/3/7  13:59
 */
public class Demo04 {
    private double x, y;
    final StampedLock sl = new StampedLock();


    public static void main(String[] args) {
        final StampedLock stampedLock = new StampedLock();
        // 乐观读
        long optimisticRead = stampedLock.tryOptimisticRead();

        // 相关操作
        // 是否有别的线程存在写行为，如果存在则 stampedLock.validate(optimisticRead)会返回false
        if (!stampedLock.validate(optimisticRead)) {
            // 乐观读升级为悲观读锁
            optimisticRead = stampedLock.readLock();
            try {
                //相关操作
                System.out.println("相关操作");
            } finally {
                stampedLock.unlockRead(optimisticRead);
            }
        }

    }

}

class Point {
    private double x, y;
    private final StampedLock sl = new StampedLock();

    void move(double deltaX, double deltaY) { // an exclusively locked method
        long stamp = sl.writeLock();
        try {
            x += deltaX;
            y += deltaY;
        } finally {
            sl.unlockWrite(stamp);
        }
    }

    /**
     * 使用了乐观读锁tryOptimisticRead();
     *
     * @return
     */
    double distanceFromOrigin() {

        long stamp = sl.tryOptimisticRead();
        double currentX = x, currentY = y;
        // 如果验证失败 则使用读锁
        if (!sl.validate(stamp)) {
            stamp = sl.readLock();
            try {
                currentX = x;
                currentY = y;
            } finally {
                sl.unlockRead(stamp);
            }
        }
        return Math.sqrt(currentX * currentX + currentY * currentY);
    }

    /**
     * 悲观读锁升级为写锁     stamp 会发生变化
     *
     * @param newX
     * @param newY
     */
    void moveIfAtOrigin(double newX, double newY) { // upgrade
        //
        long stamp = sl.readLock();
        try {
            while (x == 0.0 && y == 0.0) {
                //锁升级
                long ws = sl.tryConvertToWriteLock(stamp);
                if (ws != 0L) {
                    stamp = ws;
                    x = newX;
                    y = newY;
                    break;
                } else {
                    sl.unlockRead(stamp);
                    // 获取写锁
                    stamp = sl.writeLock();
                }
            }
        } finally {
            sl.unlock(stamp);
        }
    }
}